﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
using System.Windows.Forms.DataVisualization.Charting;
using System.Text;
using System.Timers;
using System.Diagnostics;
using System.IO;
using System.Xml;

namespace PerfTabControl
{
    class PerfTabControl : TabControl
    {
        public void LoadTemplate()
        {
            //todo: this should be moved outside of the method and the method should take in the file path. Clearing should also be an option

            //todo: make the method take a clear option, for now it defaults to on

            bool clearcurrent = true;
            if (clearcurrent)
            {
                foreach (PerfTabPage pg in this.TabPages)
                {
                    pg.Stop();
                    this.TabPages.Remove(pg);
                }
            }


            OpenFileDialog diag = new OpenFileDialog();
            DialogResult open = diag.ShowDialog();
            string path = diag.FileName;
            FileStream reader = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read);


            XmlDocument doc = new XmlDocument();
            doc.Load(reader);
            reader.Close();

            foreach (XmlNode tab in doc.ChildNodes)
            {
                //process tabs
                string tabname = (tab.Attributes["Name"].Value);
                PerfTabPage page = new PerfTabPage(tabname, true);
                this.TabPages.Add(page);
                
              
                //process charts
                foreach (XmlNode chart in tab.ChildNodes)
                {
                    PerfChart thischart = new PerfChart();
                    page.AddChart(thischart);
                    //(chart.Attributes["name"].Value);
                    foreach (XmlNode counter in chart.ChildNodes)
                    {

                        string cat = counter.Attributes["Category"].Value;
                        string name = counter.Attributes["Name"].Value;
                        string instancetype = counter.Attributes["InstanceType"].Value;
                        
                        Counter ctr;
                        string[] instances;
                        PerformanceCounterCategory pcc;

                        switch (instancetype)
                        {
                            case "Named":
                                string instance = counter.Attributes["Instance"].Value;
                                ctr = new Counter(new PerformanceCounter(cat, name, instance));
                                page.AddCounterToSelected(ctr);
                                break;
                            case "ExcludeTotal":
                                pcc = new PerformanceCounterCategory(cat);
                                instances = pcc.GetInstanceNames();
                                foreach (string item in instances)
                                {
                                    if (item != "_Total")
                                    {
                                        ctr = new Counter(new PerformanceCounter(cat, name, item));
                                        page.AddCounterToSelected(ctr);
                                    }
                                }
                                break;
                            case "All":
                                pcc = new PerformanceCounterCategory(cat);
                                instances = pcc.GetInstanceNames();
                                foreach (string item in instances)
                                {
                                    ctr = new Counter(new PerformanceCounter(cat, name, item));
                                    page.AddCounterToSelected(ctr);
                                }
                                break;
                            case "None":
                                ctr = new Counter(new PerformanceCounter(cat,name));
                                page.AddCounterToSelected(ctr);
                                break;
                           

                        }
                    }
                }
                this.SelectedTab = page;
                page.Start();
            }
            
        }
    }
    
    class PerfTabPage : TabPage
    {
        #region 4 - Member Fields
        private int _SelectedIndex = -1;
        private System.Timers.Timer _timer;
        protected delegate void AddDataDelegate();
        protected AddDataDelegate addDataDel;
        #endregion
        #region 4 - Constructor
        public PerfTabPage()
            :base()
        {
        }
        public PerfTabPage(string Name)
            : base(Name)
        {

        }
        public PerfTabPage(bool AutoSelectNewPerfChart)
            : base()
        {
            this.AutoSelectNewPerfChart = AutoSelectNewPerfChart;
        }
        public PerfTabPage(string Name, bool AutoSelectNewPerfChart)
            : base(Name)
        {
            this.AutoSelectNewPerfChart = AutoSelectNewPerfChart;
        }

        #endregion
        #region 5 - PrivateMethods
        #region 2 - Thread for perf
        private void AddDataThreadLoop(object sender, ElapsedEventArgs e) //timer invocation
        {
            ChartTab.Invoke(addDataDel);
        }
        private void AddData() //worker thread
        {
            DateTime timestamp = DateTime.Now;
            foreach (Counter ctr in ChartTab.Series)
            {
                //update series
                
                float val = ctr.PerfCounter.NextValue();
                ctr.Points.AddXY(timestamp.ToOADate(), val);
                double removeBefore = timestamp.AddSeconds((double)(110) * (-1)).ToOADate();
                while (ctr.Points[0].XValue < removeBefore)
                {
                    ctr.Points.RemoveAt(0);
                }

                //Add point
                PerfChart Chart = (PerfChart)ChartTab.ChartAreas[ctr.ChartArea];
                Chart.AxisX.Minimum = ctr.Points[0].XValue;
                Chart.AxisY.Maximum = double.NaN;
                Chart.AxisX.Maximum = DateTime.FromOADate(ctr.Points[0].XValue).AddMinutes(2).ToOADate();

                //update chart
                ChartTab.Invalidate();


            }
        }
        #endregion
        protected void SetSelected(PerfChart chart)
        {
            if (_SelectedIndex != -1)
            {
                //unselect current
                PerfChart current = (PerfChart)ChartTab.ChartAreas[_SelectedIndex];
                current.BorderDashStyle = ChartDashStyle.NotSet;
                current.BorderWidth = 1;
            }
            //new selected
            chart.BorderDashStyle = ChartDashStyle.Solid;
            chart.BorderWidth = 3;
            
        }
        #endregion
        #region 5 - Properties
        public Chart ChartTab { get; private set; }
        public int SelectedIndex
        {
            get { return _SelectedIndex;}
            set { _SelectedIndex = value; }
        }
        public PerfChart SelectedPerfChart
        {
            get { return (PerfChart)ChartTab.ChartAreas[_SelectedIndex]; }
            set 
            {
                SetSelected(value);
                _SelectedIndex= ChartTab.ChartAreas.IndexOf(value.Name);
            }
        }
        public bool AutoSelectNewPerfChart { get; set; }
        public bool Started { get; private set; }
        #endregion
        #region 4 - Methods
        public void AddChart(PerfChart chart)
        {
            
            ChartTab.ChartAreas.Add(chart);
            if (AutoSelectNewPerfChart)
            {
                SelectedPerfChart = chart;
            }
        }
        public void AddCounterToSelected(Counter ctr)
        {
            //todo: check to make sure the counter isnt already there (chart or chartarea?)
            string name = SelectedPerfChart.Name;
            Legend newleg;
            if (ChartTab.Legends.FindByName(name) == null)
            {
                newleg = new Legend(name);
                newleg.IsDockedInsideChartArea = false;
                newleg.Docking = Docking.Bottom;
                newleg.DockedToChartArea = name;
                //LegendCellColumn colname;
                ChartTab.Legends.Add(newleg);
            }
            string ctrlegname="";
            if (ctr.PerfCounter.MachineName != ".") { ctrlegname = ctr.PerfCounter.MachineName + " - "; }
            ctrlegname += ctr.PerfCounter.CounterName;
            if (ctr.PerfCounter.InstanceName != "") { ctrlegname += " - " + ctr.PerfCounter.InstanceName; }
            ctr.ChartArea = name;
            ctr.Legend = name;
            ctr.Name = ctrlegname;
            ChartTab.Series.Add(ctr);
            
        }
        public void Start()
        {
            if (!Started)
            {
                _timer.Start();
                Started = true;
            }
        }
        public void Stop()
        {
            if (Started)
            {
                _timer.Stop();
                Started = false;
            }
        }
        #region mouse click methods (events - ChartTab_MouseClick)
        public void HighLightSeries(Series SelectedSeries)
        {
            PerfChart area = (PerfChart)ChartTab.ChartAreas[SelectedSeries.ChartArea];
            string current = area.SelectedSeriesName;
            if (SelectedSeries.Name != current)
            {
                SelectedSeries.BorderWidth = 3;
                if (current != null)
                {
                    Series CurrentSeries = ChartTab.Series[current];
                    CurrentSeries.BorderWidth = 1;
                }
                area.SelectedSeriesName = SelectedSeries.Name;
            }
        }
        public void RemoveCounter(Series SelectedSeries)
        {
            //todo: check to see if its the last counter and stop timer
            PerfChart area = (PerfChart)ChartTab.ChartAreas[SelectedSeries.ChartArea];
            ChartTab.Series.Remove(SelectedSeries);
            if (area.SelectedSeriesName == SelectedSeries.Name)
            {
                area.SelectedSeriesName = null;
            }
            if (ChartTab.Series.Count == 0)//stop timer if last counter
            {
                this.Stop();
            }
        }
        #endregion
        #endregion
        #region 3 - Events
        void ChartTab_MouseClick(object sender, MouseEventArgs e)
        {

            //get all important info left/right legend/chartarea/series/item click

            HitTestResult result = ChartTab.HitTest(e.X, e.Y);
            bool RightClick = e.Button == System.Windows.Forms.MouseButtons.Right ? true : false; //was it a right click
            // dont need? PerfChart area; //= (PerfChart)result.ChartArea; - use to work, need to get chartarea again

            ContextMenu m = new ContextMenu();
            MenuItem[] mitems = new MenuItem[7];

            /* context menu items to impliment
             * 0 - chartarea
             * 1 - remove chart
             * 2 - change time span (+-)
             * 3 - change rate
             * 4- legend/series
             * 5 - highlight
             * 6 - remove
             */
            mitems[0] = new MenuItem("-Chart Options-"){Enabled=false};
            mitems[1] = new MenuItem("Remove Chart");
            mitems[2] = new MenuItem( "Change Time Span");
            mitems[3] = new MenuItem("Change Rate");

            mitems[4] = new MenuItem("-Counter Options-") { Enabled = false, BarBreak = true };
            mitems[5] = new MenuItem("Highlight") { Enabled = false };
            mitems[6] = new MenuItem("Remove Counter") { Enabled = false };
            

            #region Switch Statement for area clicked
            switch (result.ChartElementType)
            {
                case ChartElementType.Axis:
                    //
                    break;
                case ChartElementType.DataPoint:
                    //
                    break;
                case ChartElementType.LegendArea:
                    //
                    break;
                case ChartElementType.LegendItem:
                    //highlight the selected series
                    LegendItem legenditem = (LegendItem)result.Object;
                    Series selSeries = ChartTab.Series[legenditem.Name];
                    if (RightClick)
                    {
                        //RemoveCounter(selSeries);
                        mitems[5].Enabled = true;
                        mitems[5].Click += delegate(object xsender, EventArgs xe) { HighLightSeries(selSeries); };
                        mitems[6].Enabled = true;
                        mitems[6].Click += delegate(object xsender, EventArgs xe) { RemoveCounter(selSeries); };
                    }
                    else
                    {
                        HighLightSeries(selSeries);
                    }

                    break;
                case ChartElementType.PlottingArea:
                    //
                    break;
                case ChartElementType.StripLines:
                    //
                    break;
                case ChartElementType.TickMarks:
                    //
                    break;
                case ChartElementType.Title:
                    //
                    break;
            }
            #endregion
            if (RightClick)
            {
                m.MenuItems.AddRange(mitems);
                m.Show(this, new Point(e.X, e.Y));
            }





        }
        protected override void OnCreateControl() // basically onload
                {
                    base.OnCreateControl();
                    //create and define default chart
                    ChartTab = new Chart { Dock = DockStyle.Fill,BackColor = System.Drawing.Color.Transparent };
                    this.Controls.Add(ChartTab);
                    ChartTab.MouseClick += new MouseEventHandler(ChartTab_MouseClick);
                    
                    /*
                    PerfChart newchart = new PerfChart();
                    ChartTab.ChartAreas.Add(newchart);
                    
                    SelectedPerfChart = newchart; //todo: might be broken
                     */

                    //prep timer
                    _timer = new System.Timers.Timer(1000);
                    _timer.Elapsed += new ElapsedEventHandler(AddDataThreadLoop);
                    addDataDel += new AddDataDelegate(AddData);
                }
        protected override void Dispose(bool disposing)
        {
            if (_timer.Enabled == true)
            {
                _timer.Stop();
            }
            base.Dispose(disposing);
        }
        #endregion
    }
    class PerfChart : ChartArea
    {
        #region 0 - Member Fields

        #endregion
        #region 1 - Constructor
        public PerfChart()
            : base()
        {
            DateTime minvalue = DateTime.Now;
            DateTime maxvalue = minvalue.AddSeconds(120);//todo: change to user controlled

            this.AxisX.Minimum = minvalue.ToOADate();
            this.AxisX.Maximum = maxvalue.ToOADate();
            this.AxisX.LabelStyle.Format = "h:mm:ss tt";
            this.AxisX.MajorGrid.LineColor = System.Drawing.Color.LightGray;

            this.AxisY.Maximum = double.NaN;
            this.AxisY.MajorGrid.LineColor = System.Drawing.Color.LightGray;
        }
        #endregion
        #region 0 - PrivateMethods
        #endregion
        #region 1 - Properties
        public string SelectedSeriesName { get; set; }
        #endregion
        #region 0 - Methods

        #endregion

    }
    class Counter : Series
    {
        #region 0 - Member Fields

        #endregion
        #region 2 - Constructor 
        public Counter()
            :base()
        {
            this.XValueType = ChartValueType.Time;
            this.ChartType = SeriesChartType.Line;
        }
        public Counter(PerformanceCounter PerfCounter)
            : base()
        {
            this.PerfCounter = PerfCounter;
            this.XValueType = ChartValueType.Time;
            this.ChartType = SeriesChartType.Line;
        }
        #endregion
        #region 0 - PrivateMethods
        #endregion
        #region 1 - Properties
        public PerformanceCounter PerfCounter { get; set; }
        #endregion
        #region 0 - Methods

        #endregion

    }
}
